// TextSample.cpp: implementation of the CTextSample class.
//
//////////////////////////////////////////////////////////////////////

#include "ItgImage.h"
#include "TextSample.h"
#include "imageProc.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CTextSample::CTextSample()
{
	m_pimgsrc = NULL;
	m_pimgitg = NULL;
	m_pimgvie = NULL;
	m_pimghie = NULL;
	m_pimgdir = NULL;

	m_bNew = false;
}

CTextSample::~CTextSample()
{
	Clear();
}

void CTextSample::ClearImage()
{
	if (m_bNew)
	{
		if (m_pimgsrc != NULL) delete m_pimgsrc;
		if (m_pimgitg != NULL) delete m_pimgitg;
		if (m_pimgvie != NULL) delete m_pimgvie;
		if (m_pimghie != NULL) delete m_pimghie;
		if (m_pimgdir != NULL) delete m_pimgdir;
		m_bNew = false;
	}
	m_pimgsrc = NULL;
	m_pimgitg = NULL;
	m_pimgvie = NULL;
	m_pimghie = NULL;
	m_pimgdir = NULL;
}

void CTextSample::Clear()
{
	ClearImage();

	m_vfMean.resize(0);
	m_vfSTD.resize(0);
	m_vfHist.resize(0);
	m_vfEdge.resize(0);
}

/* Jiadi Yang
bool CTextSample::LoadImage(const char *strFileName)
{
	Raster_Color<uchar> img;
	
	img.LoadPpmPGm(strFileName);

	ClearImage();

	m_rect.left = 0;				m_rect.top = 0;
	m_rect.right = img.cols()-1;	m_rect.bottom = img.rows()-1;
	h = m_rect.Height();
	w = m_rect.Width();
	margin = h/8;
	int x, y, val;

	m_pimgsrc = new Raster<uchar>;
	m_pimgsrc->SetDimension(m_rect.Height(), m_rect.Width());

	for (y = 0; y < h; y++)
		for (x = 0; x < w; x ++)
		{
			val = (30 * img.R(y, x) + 59 * img.G(y,x) + 11 * img.B(y,x) + 50)/100;
			(*m_pimgsrc)(y, x) = val;
		}

	m_pimgitg = new McItgImage;
	m_pimgitg->Prepare(*m_pimgsrc);

	m_pimgvie = new Raster<int>;
	m_pimgvie->SetDimension(m_rect.Height(), m_rect.Width());
	m_pimgvie->InitValue(0);

	m_pimghie = new Raster<int>;
	m_pimghie->SetDimension(m_rect.Height(), m_rect.Width());
	m_pimghie->InitValue(0);

	m_pimgdir = new Raster<uchar>;
	m_pimgdir->SetDimension(m_rect.Height(), m_rect.Width());

	BYTE *pbsrc = &((*m_pimgsrc)(0));
	BYTE *pbdir = &((*m_pimgdir)(0));
	int *pnh = &((*m_pimghie)(0));
	int *pnv = &((*m_pimgvie)(0));
	Sobel(pbsrc, w, h, pnh, pnv, pbdir);

	m_bNew = TRUE;

	return TRUE;
}
*/

void CTextSample::SetImages(Raster<uchar> *psrc, McItgImage *pitg, Raster<int> *pvie, Raster<int> *phie)
{
	ClearImage();

	m_pimgsrc = psrc;
	m_pimgitg = pitg;
	m_pimgvie = pvie;
	m_pimghie = phie;
}

int CTextSample::CalcuMeanFeatures()
{
	m_vfMean.resize(0);
	McItgImage &itgimg = *m_pimgitg;

	// 0 -- m
	irmm_Rect rt = irmm_Rect(1,margin,w-1,h-margin) + m_rect.TopLeft();
	m = (float)itgimg.RectMean(rt);
	m_vfMean.push_back(m);

	// 1 -- m1   m1/m/m2
	rt = irmm_Rect(1,0,w-1,margin) + m_rect.TopLeft();
	float m1 = (float)itgimg.RectMean(rt);
	m_vfMean.push_back(m1);

	// 2 -- m2
	rt = irmm_Rect(1,h-margin,w-1,h) + m_rect.TopLeft();
	float m2 = (float)itgimg.RectMean(rt);
	m_vfMean.push_back(m2);

	// 3 -- combination of m, m1, m2
	m_vfMean.push_back( (m1-m) * (m2-m) );

	return m_vfMean.size();
}


int CTextSample::CalcuSTDFeatures()
{
	m_vfSTD.resize(0);
	McItgImage &itgimg = *m_pimgitg;

	int nfea = 0;

	// 0 -- s
	irmm_Rect rt = irmm_Rect(1,margin,w-1,h-margin) + m_rect.TopLeft();
	s = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back(s);

	nfea ++;
	// 1 -- s1 / s / s2
	rt = irmm_Rect(1,0,w-1,margin) + m_rect.TopLeft();
	float s1 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back(s1);

	nfea ++;
	// 2 -- s1 / s / s2
	rt = irmm_Rect(1,h-margin,w-1,h) + m_rect.TopLeft();
	float s2 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back(s2);

	nfea ++;
	// 3
	m_vfSTD.push_back( s1/s );
	nfea ++;
	// 4
	m_vfSTD.push_back( s2/s );

	nfea ++;
	// 5 -- s1 / s2
	rt = irmm_Rect(1,margin,w-1,h/2) + m_rect.TopLeft();
	s1 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back( s1/s );
	nfea ++;
	// 6
	rt = irmm_Rect(1,h/2,w-1,h-margin) + m_rect.TopLeft();
	s2 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back( s2/s );

	nfea ++;
	// 7 -- s1 | s2
	rt = irmm_Rect(1,margin,w/2,h-margin) + m_rect.TopLeft();
	s1 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back( s1/s );
	nfea ++;
	// 8
	rt = irmm_Rect(w/2, margin, w-1,h-margin) + m_rect.TopLeft();
	s2 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back( s2/s );

	nfea ++;
	// 9 -- s1 | s2 | s3
	rt = irmm_Rect(1,margin,w/3,h-margin) + m_rect.TopLeft();
	s1 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back( s1/s );
	nfea ++;
	// 10 -- s2
	rt = irmm_Rect(w/3,margin,w*2/3,h-margin) + m_rect.TopLeft();
	s2 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back( s2/s );
	nfea ++;
	// 11 -- s3
	rt = irmm_Rect(w*2/3,margin,w-1,h-margin) + m_rect.TopLeft();
	float s3 = (float)itgimg.CalcuSTD(rt);
	m_vfSTD.push_back( s3/s );

	return m_vfSTD.size();
}

int CTextSample::CalcuHistFeatures()
{
	m_vfHist.resize(0);
	
	// Histogram
	// peak value and peak location
	float hist[16];
	int hist256[256], nbin = 16;
	memset(hist256, 0, sizeof(int) * 256);
	int imax = 0, imin = 256;
	for (int y = margin; y < h-margin; y ++)
	{
		int y1 = y + m_rect.top;
		for (int x = 1; x < w-1; x ++)
		{
			int x1 = x + m_rect.left;
			uchar val = m_pimgsrc->Data(y1, x1);
			if (val > imax) imax = val;
			if (val < imin) imin = val;
			hist256[val] ++;
		}
	}
	memset(hist, 0, sizeof(float) * nbin);
	float a = (float)255.0 / nbin / (imax - imin);
	int i;
	for (int i = 0; i < 256; i++)
	{
		int val = hist256[i];
		if (!val) continue;
		hist[(int)(a * (i-imin))] += val;
	}

	imax = 0;
	for (i = 0; i < nbin; i ++)
	{
		if (imax < hist[i])
		{
			imax = (int)hist[i];
			imin = i;
		}
	}
	float peakval = (float)imax/((w-2)*(h-margin*2));
	int   peakidx = imin;

	// Histogram }}

	// 0
	m_vfHist.push_back(peakval);
	// 1
	m_vfHist.push_back(imin);
#if 0
	// 2-17
//	for (i = 0; i < nbin; i++)
//	{
//		m_vfHist.push_back(hist[i]/((w-2)*(h-margin*2)));
//	}

	//
	// calculate histogram of intensity gradient magnitude
	//
	nbin = 16;

	memset(hist, 0, sizeof(float) * nbin);
	for (y = margin; y < h-margin; y ++)
	{
		int y1 = y + m_rect.top;
		for (int x = 1; x < w-1; x ++)
		{
			int x1 = x + m_rect.left;
			BYTE val = m_pimgsrc->Pixel(x1, y1);
			BYTE valx = m_pimgsrc->Pixel(x1+1, y1);
			BYTE valy = m_pimgsrc->Pixel(x1, y1+1);

//			double v = sqrt((val-valx)*(val-valx)+(val-valy)*(val-valy))/nbin + 0.5;
//			hist[(int)v] ++;

			irmm_Rect rt = irmm_Rect(1,margin,w-1,h-margin) + m_rect.TopLeft();
			s = m_pimgitg->CalcuSTD(rt);
			double v = sqrt((val-valx)*(val-valx)+(val-valy)*(val-valy))/s*4 + 0.5;
			int idx = (v>16)?16:v;
			hist[(int)idx] ++;
		}
	}

	for (i = 0; i < nbin; i++)
	{
		m_vfHist.push_back(hist[i]/((w-2)*(h-margin*2)));
	}
#endif
	return m_vfHist.size();
}

int CTextSample::CalcuEdgeFeatures()
{
	m_vfEdge.resize(0);

	// {{ Edge

	// backup the edge maps
	Raster<int> vieimg,hieimg;
	vieimg.SetDimension(m_pimgvie->Height(), m_pimgvie->Width());
	hieimg.SetDimension(m_pimghie->Height(), m_pimghie->Width());
	// record count of edge pixels on the vertical edge with length bigger than ..
	double pctv[] = {0.001, 0.1, 0.2, 0.3, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.999};
	int vbin = 12;
	// record count of edge pixels on the horizontal edge with length bigger than ..
	double	pcth[] = {0.001, 0.1, 0.2, 0.3, 0.5, 0.7, 0.999};
	int		hbin = 7;
	float	hedgecnt[7], vedgecnt[12], pfe[6];
	irmm_Rect	rect;

	int n = (w-2) * (h-margin*2);

	for (int k = 2; k <= 2; k++)
	{
		rect.Set(m_rect.left, m_rect.top, m_rect.right, m_rect.bottom);
		m_pimgvie->CopySubImage(rect, vieimg, rect);
		m_pimghie->CopySubImage(rect, hieimg, rect);

		float Std = s*k;

		// count vertical edges
		memset(vedgecnt, 0, sizeof(float) * vbin);
		// Notice:  all the sample images have one pixel width margin for sobel edge filer
		int y, y1, x, x1, i, j;
		for ( y = m_rect.top + margin; y < m_rect.bottom-margin; y ++)
		{
			for ( x = m_rect.left + 1; x < m_rect.right-1; x ++)
			{
				if (abs(vieimg(y, x)) <= Std) continue;
				int x0 = x;
				int nv = 1;
				for ( y1 = y + 1; y1 < m_rect.bottom-margin; y1 ++)
				{
					for ( x1 = x0-1; x1 <= x0+1; x1++)
					{
						if (x1 < m_rect.left + 1 || x1 >= m_rect.right - 1)
							continue;
						if (abs(vieimg(y1, x1)) > Std)
						{
							x0 = x1;
							nv ++;
							break;
						}
					}
					if (x1 > x0+1) break;
				}
				double pct = (double)nv / (h-margin*2);
				for ( i = 0; i < vbin; i++)
				{
					if (pct < pctv[i]) break;
					vedgecnt[i] += nv;
				}

				// Assign the edge pixel value according to it's length index
				if (i>=Std)
					i = 0;
				vieimg(y, x) = i;
				x0 = x;
				for (y1 = y + 1; y1 < m_rect.bottom-margin; y1 ++)
				{
					for ( x1 = x0-1; x1 <= x0+1; x1++)
					{
						if (x1 < m_rect.left + 1 || x1 >= m_rect.right - 1)
							continue;
						if (abs(vieimg(y1, x1)) > Std)
						{
							x0 = x1;
							vieimg(y1, x1) = i;
							break;
						}
					}
					if (x1 > x0+1) break;
				}
			}
		}

		// normalize edge count
		for ( j = 0; j < vbin; j ++)
		{
			vedgecnt[j] /= sqrt((float)n);
			m_vfEdge.push_back(vedgecnt[j]);
		}

		// count horizontal edges
		memset(hedgecnt, 0, sizeof(float) * hbin);
		// Notice:  all the sample images have one pixel width margin for sobel edge filer
		for (y = m_rect.top+margin; y < m_rect.bottom-margin; y ++)
		{
			for ( x = m_rect.left+1; x < m_rect.right-1; x ++)
			{
				if (abs(hieimg(y, x)) <= Std) continue;
				int y0 = y;
				int nh = 1;
				for ( x1 = x + 1; x1 < m_rect.right-1; x1 ++)
				{
					for ( y1 = y0-1; y1 <= y0+1; y1++)
					{
						if (y1 < m_rect.top+margin || y1 >= m_rect.bottom - margin - 1)
							continue;
						if (abs(hieimg(y1, x1)) > Std)
						{
							y0 = y1;
							nh ++;
							break;
						}
					}
					if (y1 > y0+1) break;
				}
				double pct = (double)nh / (h-margin*2);
				for ( i = 0; i < hbin; i++)
				{
					if (pct < pcth[i]) break;
					hedgecnt[i] += nh;
				}

				// Assign the edge pixel value according to it's length index
				if (i>=Std)
					i = 0;
				hieimg(y, x) = i;
				y0 = y;
				for (x1 = x + 1; x1 < m_rect.right-1; x1 ++)
				{
					for ( y1 = y0-1; y1 <= y0+1; y1++)
					{
						if (y1 < m_rect.top+margin || y1 >= m_rect.bottom - margin - 1)
							continue;
						if (abs(hieimg(y1, x1)) > Std)
						{
							y0 = y1;
							hieimg(y1, x1) = i;
							break;
						}
					}
					if (y1 > y0+1) break;
				}
			}
		}

		// normalize edge count
		for (j = 0; j < hbin; j ++)
		{
			hedgecnt[j] /= sqrt((float)n);
			m_vfEdge.push_back(hedgecnt[j]);
		}

		// count vertical edges in blocks
		// Notice:  all the sample images have one pixel width margin for sobel edge filer
		float fe, fhe0, fhe1, fhe2, fve0, fve1, fve2;
		fe = fhe0 = fhe1 = fhe2 = fve0 = fve1 = fve2 = 0;
		for (y = m_rect.top+1; y < m_rect.bottom-1; y ++)
		{
			for (int x = m_rect.left+1; x < m_rect.right-1; x ++)
			{
				int val = vieimg(y, x);
				if (abs(val) >= vbin && abs(val) <= Std) continue;
				if (abs(val) < vbin)
					fe ++;
				// e0 / e1 / e2
				if (y <= m_rect.top+margin) // e0
					fve0 ++;
				else if (y >= m_rect.bottom-1-margin) // e2
					fve2 ++;
				else // e1
					fve1 ++;
				// e0 | e1 | e2
				if (y < m_rect.top+margin) continue;
				if (y >= m_rect.bottom-margin) continue;
				if (x <= m_rect.left+w/3)
					fhe0 ++;
				else if (x >= m_rect.left+w*2/3)
					fhe2 ++;
				else
					fhe1 ++;
			}
		}
		fe /= sqrt((float)n);
		fve0 /= sqrt((float)((w-2)*margin));
		fve1 /= sqrt((float)((w-2)*(h-2-margin*2)));
		fve2 /= sqrt((float)((w-2)*margin));
		fhe0 /= sqrt((float)((h-margin*2) * (w/3)));
		fhe1 /= sqrt((float)((h-margin*2) * (w*2/3 - w/3 - 1)));
		fhe2 /= sqrt((float)((h-margin*2) * (w - 1 - w*2/3)));
		pfe[0] = fhe0 / fe;
		pfe[1] = fhe1 / fe;
		pfe[2] = fhe2 / fe;
		pfe[3] = fve0 / fe;
		pfe[4] = fve1 / fe;
		pfe[5] = fve2 / fe;

		for (j = 0; j < 6; j++)
			m_vfEdge.push_back(pfe[j]);
	}
	return m_vfEdge.size();
}

int CTextSample::GetAllFeatures(vfloat &vfFeatures, vfeaid &vFeaID)
{
	vfFeatures.resize(0);
	vFeaID.resize(0);

	CFeaID fid;
	int i;

	for (int i = 0; i < m_vfMean.size(); i++)
	{
		fid.strFeaName[0] = 'M';
		fid.strFeaName[1] = 'U';
		fid.nFeaIdx = i;
		vFeaID.push_back(fid);
		vfFeatures.push_back(m_vfMean[i]);
	}

	for (i = 0; i < m_vfSTD.size(); i++)
	{
		fid.strFeaName[0] = 'S';
		fid.strFeaName[1] = 'D';
		fid.nFeaIdx = i;
		vFeaID.push_back(fid);
		vfFeatures.push_back(m_vfSTD[i]);
	}

	for (i = 0; i < m_vfHist.size(); i++)
	{
		fid.strFeaName[0] = 'H';
		fid.strFeaName[1] = 'T';
		fid.nFeaIdx = i;
		vFeaID.push_back(fid);
		vfFeatures.push_back(m_vfHist[i]);
	}

	for (i = 0; i < m_vfEdge.size(); i++)
	{
		fid.strFeaName[0] = 'E';
		fid.strFeaName[1] = 'G';
		fid.nFeaIdx = i;
		vFeaID.push_back(fid);
		vfFeatures.push_back(m_vfEdge[i]);
	}

	return vfFeatures.size();
}

//
// input: vFeaID
// output:vfFeatures
// get an array of features specified by vFeaID
int CTextSample::GetFeatures(vfloat &vfFeatures, vfeaid &vFeaID)
{
	vfFeatures.resize(0);
	for (int i = 0; i < vFeaID.size(); i ++)
	{
		CFeaID &fid = vFeaID[i];
		if (fid.strFeaName[0] == 'M' &&
			fid.strFeaName[1] == 'U')
		{
			if (m_vfMean.size() == 0)
				CalcuMeanFeatures();
			vfFeatures.push_back(m_vfMean[fid.nFeaIdx] );
		}
		else if (fid.strFeaName[0] == 'S' &&
				 fid.strFeaName[1] == 'D')
		{
			if (m_vfSTD.size() == 0)
				CalcuSTDFeatures();
			vfFeatures.push_back(m_vfSTD[fid.nFeaIdx] );
		}
		else if (fid.strFeaName[0] == 'H' &&
				 fid.strFeaName[1] == 'T')
		{
			if (m_vfHist.size() == 0)
				CalcuHistFeatures();
			vfFeatures.push_back(m_vfHist[fid.nFeaIdx] );
		}
		else if (fid.strFeaName[0] == 'E' &&
				 fid.strFeaName[1] == 'G')
		{
			if (m_vfEdge.size() == 0)
				CalcuEdgeFeatures();
			vfFeatures.push_back(m_vfEdge[fid.nFeaIdx] );
		}
	}
	return vfFeatures.size();
}


void CTextSample::CalcuAllFeatures()
{
	CalcuMeanFeatures();
	CalcuSTDFeatures();
	CalcuHistFeatures();
	CalcuEdgeFeatures();
}


void CTextSample::CalcuNonEdgeFeatures()
{
	CalcuMeanFeatures();
	CalcuSTDFeatures();
	CalcuHistFeatures();
}
